//marine lua

if not CC_TrickSkins
	rawset(_G, "CC_TrickSkins", {})
end
CC_TrickSkins["marine"] = {
	{sprite2 = SPR2_SPNG,
	rotation = "random",
	tics = 0},
	{sprite2 = SPR2_EDGE,
	rarity = 2*FRACUNIT,
	rotation = "random",
	tics = 0,
	frames = 1},
	{sprite2 = SPR2_MLEL,
	rotation = 1,
	tics = 0,
	frames = 1},
	{sprite2 = SPR2_SWIM,
	rotation = "random",
	tics = 0,
	frames = 1}
}

//soc

local function SafeFreeslot(...)
	for _, item in ipairs({...})
		if rawget(_G, item) == nil
			freeslot(item)
		end
	end
end

SafeFreeslot(
	"SKINCOLOR_MATEY",
	"MT_MARINE_PUNCH",
	"MT_MARINE_BUBBLE",
	"MT_MARINE_SUPER",
	"MT_MARINE_FLOAT",
	"MT_MARINE_HIT",
	"S_MARINE_FLUTTER",
	"S_MARINE_SWIPE",
	"S_MARINE_SHOOT",
	"S_MARINE_AERIAL",
	"S_MARINE_PUNCH",
	"S_MARINE_BUBBLE",
	"S_MARINE_LARGE",
	"S_MARINE_SUPER",
	"S_MARINE_FLOAT",
	"S_MARINE_HIT",
	"S_MARINE_FLIP",
	"S_MARINE_FLIP2",
	"S_MARINE_JOJOKE",
	"S_MARINE_JOJOKE_AURA",
	"SPR_SOLB",
	"sfx_pmarin",
	"sfx_tmarin",
	"sfx_fmarin",
	"sfx_bmarin",
	"sfx_cmarin",
	"sfx_jmarin")

skincolors[SKINCOLOR_MATEY] = {
    name = "Matey",
	ramp = {88,89,64,51,52,54,56,58,60,43,44,45,45,46,46,47},
	invcolor = SKINCOLOR_MINT,
	invshade = 8, 
	chatcolor = V_ORANGEMAP,
	accessible = true}

sfxinfo[sfx_pmarin].caption = "P-Speed"	
sfxinfo[sfx_tmarin].caption = "Raccoon tail"
sfxinfo[sfx_fmarin].caption = "Raccoon flight"
sfxinfo[sfx_bmarin].caption = "Sol buster"
sfxinfo[sfx_cmarin].caption = "Charging buster"
sfxinfo[sfx_jmarin].caption = "Bubble flip"

mobjinfo[MT_MARINE_PUNCH] = {
	doomednum = -1,
	spawnhealth = 1,
	spawnstate = S_MARINE_PUNCH,
	deathstate = S_NULL,
	speed = 64*FRACUNIT,
	radius = 32*FRACUNIT,
	height = 32*FRACUNIT,
	flags = MF_NOGRAVITY|MF_NOBLOCKMAP|MF_MISSILE,
}
	
mobjinfo[MT_MARINE_BUBBLE] = {
	doomednum = -1,
	spawnhealth = 1000,
	spawnstate = S_MARINE_BUBBLE,
	deathstate = S_NULL,
	reactiontime = 8,
	speed = 8,
	radius = 4*FRACUNIT,
	height = 4*FRACUNIT,
	mass = 16,
	flags = MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC
}
	
mobjinfo[MT_MARINE_SUPER] = {
	doomednum = -1,
	spawnhealth = 1,
	spawnstate = S_MARINE_SUPER,
	deathstate = S_NULL,
	deathsound = sfx_gasp,
	radius = 24*FRACUNIT,
	height = 24*FRACUNIT,
	mass = DMG_WATER,
	flags = MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT
}
	
mobjinfo[MT_MARINE_FLOAT] = {
	doomednum = -1,
	spawnhealth = 1000,
	spawnstate = S_MARINE_FLOAT,
	deathstate = S_NULL,
	deathsound = sfx_gasp,
	reactiontime = 32,
	speed = 32,
	radius = 24*FRACUNIT,
	height = 24*FRACUNIT,
	mass = DMG_WATER,
	flags = MF_NOGRAVITY|MF_NOBLOCKMAP|MF_SCENERY|MF_RUNSPAWNFUNC
}
	
mobjinfo[MT_MARINE_HIT] = {
	doomednum = -1,
	spawnhealth = 1,
	spawnstate = S_MARINE_HIT,
	deathstate = S_NULL,
	radius = 42*FRACUNIT,
	height = 64*FRACUNIT,
	mass = DMG_WATER,
	flags = MF_SOLID|MF_NOGRAVITY|MF_NOCLIPHEIGHT
}
	
states[S_MARINE_FLUTTER] = {SPR_PLAY, SPR2_RUN|FF_ANIMATE, -1, NULL, 4, 2, S_PLAY_FALL}
states[S_MARINE_SWIPE] = {SPR_PLAY, SPR2_MLEL, -1, NULL, 7, 2, S_PLAY_FALL}
states[S_MARINE_SHOOT] = {SPR_PLAY, SPR2_FIRE|FF_ANIMATE, 10, NULL, 5, 2, S_PLAY_STND}
states[S_MARINE_AERIAL] = {SPR_PLAY, SPR2_TWIN|FF_ANIMATE, 10, NULL, 5, 2, S_PLAY_FALL}
states[S_MARINE_PUNCH] = {SPR_SOLB, 0|FF_FULLBRIGHT|TR_TRANS30, 40, NULL, 0, 0, S_NULL}
states[S_MARINE_BUBBLE] = {SPR_BUBL, 0|FF_FULLBRIGHT|TR_TRANS50, 1, A_BubbleRise, 0, 1024, S_MARINE_BUBBLE}
states[S_MARINE_LARGE] = {SPR_BUBL, 1|FF_FULLBRIGHT|TR_TRANS50, 1, A_BubbleRise, 0, 1024, S_MARINE_LARGE}
states[S_MARINE_SUPER] = {SPR_BUBL, 3|FF_FULLBRIGHT|TR_TRANS50, 1, NULL, 0, 0, S_MARINE_SUPER}
states[S_MARINE_FLOAT] = {SPR_BUBL, 3|FF_FULLBRIGHT|TR_TRANS50, 1, A_BubbleRise, 0, 2048, S_MARINE_FLOAT}
states[S_MARINE_HIT] = {SPR_NULL, 0, -1, NULL, 0, 0, S_NULL}
states[S_MARINE_FLIP] = {SPR_PLAY, SPR2_FALL|FF_SPR2ENDSTATE, 2, nil, S_MARINE_FLIP2, 0, S_MARINE_FLIP}
states[S_MARINE_FLIP2] = {SPR_PLAY, SPR2_EDGE, 2, nil, 0, 0, S_PLAY_ROLL}

//functions

local FRACUNIT = FRACUNIT
local FRACBITS = FRACBITS
local TICRATE = TICRATE

local battler = false
local function marinebust(mobj, sector)
	for fof in sector.ffloors()
		if not (fof.flags & FF_BUSTUP) or not (fof.flags & FF_EXISTS)
			continue
		end 
		if (mobj.z+mobj.height < fof.bottomheight) or (mobj.z > fof.topheight)
			continue
		end
		EV_CrumbleChain(fof)
		local line = fof.master
		if line.flags & ML_EFFECT5
			P_LinedefExecute(P_AproxDistance(line.dx, line.dy) >> FRACBITS, mo, fof.target)
		end
		return true
	end
end

local function marineset(player)
	player.marine.used = false
	player.marine.fly = 0
	player.marine.soar = 0
	player.marine.roll = false
	player.marine.cool = 0
	player.marine.help = false
end

addHook("PlayerSpawn", function(player)
	player.marine = nil
end)

local function marineghost(player)
	local marighost = P_SpawnGhostMobj(player.mo)
	marighost.fuse = 3
	if not (leveltime%5) and not player.bot
		S_StopSoundByID(player.mo, sfx_pmarin)
		S_StartSound(player.mo, sfx_pmarin)
	end
end

local function bubbler(water, mobj)
	if not (water.z > mobj.z+mobj.height) and not (mobj.z > water.z+water.height)
		if (mobj.valid and mobj.health and (mobj.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) or (mobj.flags & MF_PUSHABLE and mobj.flags & MF_SHOOTABLE)))
			P_DamageMobj(mobj, water, water.target, DMG_WATER, 1)
		end
		if (mariomode) or (maptol & TOL_NIGHTS)
			return
		end
		if (mobj.player) and not (water.target == mobj) and not (mobj.player.powers[pw_shield] & SH_PROTECTWATER) and (mobj.player.playerstate == PST_LIVE) and not P_PlayerInPain(mobj.player) and not (mobj.state == S_PLAY_GASP)
			mobj.momx = 0
			mobj.momy = 0
			mobj.momz = 0
			P_ResetPlayer(mobj.player)
			mobj.state = S_PLAY_GASP
			if ((mobj.player.powers[pw_underwater]) and mobj.player.powers[pw_underwater] <= 11*TICRATE)
				mobj.player.powers[pw_underwater] = underwatertics
				P_RestoreMusic(mobj.player)
			end
			mobj.player.powers[pw_underwater] = underwatertics
			S_StartSound(mobj, water.info.deathsound)
			water.fuse = 1
		end
	end
end

local function redrover(marine)
	if (marine.eflags & MFE_VERTICALFLIP)
		return marine.ceilingrover
	else
		return marine.floorrover
	end
end

local function zpos(position, item)
	return (position.z + (position.height - mobjinfo[item].height)/2)
end

local function newGunLook(player) //copypaste from battle
	local twod = (twodlevel or player.mo.flags2 & MF2_TWOD)
	local ringdist, span
	if not(twod)
		ringdist = RING_DIST*2
		span = ANG30
	else
		ringdist = RING_DIST
		span = ANG20
	end
	local maxdist = FixedMul(ringdist, player.mo.scale)
	local closestdist = 0
	local closestmo = nil
	searchBlockmap("objects",function(pmo,mo)
		if not (mo.flags & MF_SHOOTABLE) return end
		if not (mo.health) return end -- dead
		if not (mo.player) and not (mo.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR)) return end
		if mo.type == MT_RING_REDBOX and not (player.ctfteam == 1) return end  //CTF monitor 
		if mo.type == MT_RING_BLUEBOX and not (player.ctfteam == 2) return end
		if (mo == pmo) return end
		if (mo.flags2 & MF2_FRET) return end
		if (mo.player and (CBW_Battle.MyTeam(player,mo.player) or mo.player.spectator)) return end //Disallow targeting teammates
		local zdist = (pmo.z + pmo.height/2) - (mo.z + mo.height/2) //Do angle/distance checks
		local dist = FixedHypot(pmo.x-mo.x, pmo.y-mo.y)
		local xyz_angle = abs(R_PointToAngle2(0, 0, dist, zdist))
		local xy_angle = abs(R_PointToAngle2(pmo.x + P_ReturnThrustX(pmo, pmo.angle, pmo.radius), pmo.y + P_ReturnThrustY(pmo, pmo.angle, pmo.radius), mo.x, mo.y) - pmo.angle)
		dist = FixedHypot(dist, zdist)
		if (dist > maxdist)
			return -- out of range
		end
		if (xyz_angle > span)
			return -- Don't home outside of desired angle!
		end
		if (twod
		and abs(pmo.y-mo.y) > pmo.radius)
			return -- not in your 2d plane
		end
		if ((closestmo and closestmo.valid) and (dist > closestdist))
			return
		end
		if (xy_angle > span)
			return -- behind back
		end
		if not (P_CheckSight(pmo, mo))
			return -- out of sight
		end
		closestmo = mo
		closestdist = dist
	end,player.mo,player.mo.x-maxdist,player.mo.x+maxdist,player.mo.y-maxdist,player.mo.y+maxdist)
	return closestmo
end

//battle

addHook("PreThinkFrame", function()
	//special
	if CBW_Battle and not battler
		local function marinespecial(mo, special)
			local mari = mo.player
			mari.actiontext = "sol buster"
			mari.actionrings = 10
			if P_PlayerInPain(mari) or mari.powers[pw_nocontrol] or (mari.playerstate != PST_LIVE)
			or not CBW_Battle.CanDoAction(mari)
				if mari.actionstate
					CBW_Battle.ApplyCooldown(mari, 2*(TICRATE))
				end
				mari.actionstate = 0
				mari.actiontime = 0
				return
			end
			if (special == 1) and not (mari.actionstate)
				mari.actionstate = 1
				mari.actiontime = 0
				CBW_Battle.PayRings(mari)
			end
			if (mari.actionstate == 1)
				mari.actiontime = $ + 1
				if mari.actiontime == 1
					S_StartSound(mo, sfx_cmarin)
				elseif mari.actiontime >= TICRATE/2
					mari.actionstate = 2
				end
				mo.momz = 0
				mari.drawangle = mo.angle
				local speed = FixedHypot(mo.momx,mo.momy)
				if speed > mo.scale then
					local dir = R_PointToAngle2(0,0,mo.momx,mo.momy)
					P_InstaThrust(mo,dir,FixedMul(speed,mo.friction))
					if P_IsObjectOnGround(mo) and not (mari.actiontime % 3)
						P_SpawnSkidDust(mari, mo.radius, true)
					end
				end
				mo.state = S_PLAY_STND
				mari.pflags = $ | PF_JUMPSTASIS
				if P_IsObjectOnGround(mo)
					mo.state = S_MARINE_SHOOT
					mari.pflags = $ | PF_STASIS
				else
					mo.state = S_MARINE_AERIAL
				end
				local lockon = newGunLook(mari)
				if (lockon and lockon.valid)
					P_SpawnLockOn(mari, lockon, S_LOCKON1)
				end
			elseif (mari.actionstate == 2)
				if P_IsObjectOnGround(mo)
					mo.state = S_MARINE_SHOOT
				else
					mo.state = S_MARINE_AERIAL
				end
				mari.marine.fly = 0
				mari.marine.soar = 0
				local punch = nil
				local lockon = newGunLook(mari)
				if (lockon and lockon.valid)
					mari.drawangle = R_PointToAngle2(mo.x, mo.y, lockon.x, lockon.y)
					punch = P_SpawnPointMissile(mo, lockon.x, lockon.y, zpos(lockon, mari.thokitem), mari.thokitem, mo.x, mo.y, zpos(mo, mari.thokitem))
				else
					punch = P_SpawnPointMissile(mo, mo.x+P_ReturnThrustX(nil, mo.angle, FRACUNIT), mo.y+P_ReturnThrustY(nil, mo.angle, FRACUNIT), zpos(mo, mari.thokitem), mari.thokitem, mo.x, mo.y, zpos(mo, mari.thokitem))
				end
				if (punch and punch.valid)
					punch.color = mari.skincolor
					punch.scale = mo.scale/2
					punch.name = "sol buster"
					punch.hit_sound = sfx_s3k39
				end
				CBW_Battle.ApplyCooldown(mari, 5*(TICRATE/2))
				P_InstaThrust(mo, mo.angle, min((-6*mo.scale), -(FixedHypot(mari.rmomx, mari.rmomy))*1/3))
				S_StartSound(mo, sfx_bmarin)
				mari.actionstate = 0
				mari.actiontime = 0
			end
		end
		//collision
		local function intermarine(player)
			if (player.realmo.state == S_MARINE_SWIPE)
				CBW_Battle.SetPriority(player,1,1,"tails_fly",0,0,"tail swipe")
			end
			if (player.panim == PA_ABILITY)
				CBW_Battle.SetPriority(player,0,0,"fang_tailbounce",0,2,"twirl float")
			end
			if ((player.realmo.state == S_MARINE_SHOOT) or (player.realmo.state == S_MARINE_AERIAL)) and (player.actionstate != 1)
				CBW_Battle.SetPriority(player,0,0,"amy_melee",2,1,"sol buster")
			end
		end
		//exhaust
		local function MarineExhaust(player)
			if player.marine and (player.marine.fly or player.marine.soar)
				local maxtime = 2*TICRATE
				player.exhaustmeter = max(0,$-FRACUNIT/maxtime)
				
				if not player.exhaustmeter
					if player.marine.run >= TICRATE*2
						player.marine.run = 0
					end
					player.marine.fly = 0
					player.marine.soar = 0
					P_ResetPlayer(player)
					player.mo.state = S_PLAY_FALL
				end
			elseif P_IsObjectOnGround(player.mo)
				player.exhaustmeter = FRACUNIT
			end
			return true
		end
		//vars
		battler = true
		CBW_Battle.SkinVars["marine"] = {
			flags = SKINVARS_GUARD,
			weight = 90,
			shields = 1,
			special = marinespecial,
			guard_frame = 1,
			func_priority_ext = intermarine,
			func_exhaust = MarineExhaust}
		print("\x87\Marine's BattleMod support has been added!")
	end
end)

//main

addHook("PlayerThink", function(player)
	if not (player.mo) or (player.mo.skin != "marine")
		if (player.marine)
			player.marine = nil
		end
		return
	end
	if not (player.marine)
		local marine = {
		used = false,
		fly = 0,
		run = 0,
		soar = 0,
		angle = 0,
		spin = 0,
		roll = false,
		cool = 0,
		help = false}
		player.marine = marine
	end
	//taunt
	if (player.solchar) and player.solchar.istransformed
		if (player.solchar.istransformed == 1)
			marineset(player) // the sol transform causes problems with literally everything so just hard reset marine
			player.marine.run = 0
			P_ResetPlayer(player)
			return
		end
		if player.powers[pw_underwater]
			player.powers[pw_underwater] = underwatertics
		end
		if (rusheffects.value) and not (leveltime%4) and (player.mo.sprite2 != FF_SPR2SUPER|SPR2_TRNS)
			local water = P_SpawnMobjFromMobj(player.mo, P_RandomRange(-24, 24)*player.mo.scale, P_RandomRange(-24, 24)*player.mo.scale, player.mo.height/2+P_RandomRange(-20, 20)*player.mo.scale, MT_MARINE_BUBBLE)
			water.color = player.mo.color
			water.colorized = true
			water.scale = P_RandomRange($, $*7/5)
			water.fuse = TICRATE*6
			if not (leveltime%34)
				water.state = S_MARINE_LARGE
			end
		end
	end
	if (player.cmd.buttons & BT_CUSTOM3) and (player.cmd.buttons & BT_TOSSFLAG) and P_IsObjectOnGround(player.mo) and player.speed == 0 and player.mo.state != S_MARINE_JOJOKE
		player.mo.state = S_MARINE_JOJOKE
		player.pflags = $|PF_JUMPED|PF_NOJUMPDAMAGE
		player.marine.wasajoke = true
	end
	if player.mo.state == S_MARINE_JOJOKE
		player.pflags = $|PF_JUMPSTASIS
		if (player.mo.frame & FF_FRAMEMASK == 5)
			player.mo.anim_duration = -1
		end
		if (player.mo.frame & FF_FRAMEMASK == 4) and not (player.mo.marine_playedstupidsound)
			player.mo.marine_playedstupidsound = true
			S_StartSound(player.mo, sfx_mchpos)
		end
		if player.speed != 0
			player.mo.state = S_PLAY_WALK
		end
	elseif (player.marine.wasajoke)
		player.mo.marine_playedstupidsound = false
		P_ResetPlayer(player)
		player.marine.wasajoke = false
	end
	if (player.mo.state == S_MARINE_SWIPE)
		if P_IsObjectOnGround(player.mo)
			player.thrustfactor = 5
		elseif player.gotflagdebuff
			player.thrustfactor = 8
		else
			player.thrustfactor = 10
		end
		player.drawangle = player.marine.angle
		player.marine.angle = $+ANGLE_22h
		player.marine.spin = $+1
		if marinebust(player.mo, player.mo.subsector.sector)
			player.mo.momz = 0
			player.mo.momy = 0
			player.mo.momx = 0
		end
		if player.cmd.forwardmove or player.cmd.sidemove
			player.marine.oldangle = player.mo.angle
		end
		if (player.marine.spin > 16)
			player.mo.state = S_PLAY_FALL
			player.pflags = $|PF_NOJUMPDAMAGE
			if player.solchar.istransformed
				S_StartSound(player.mo, sfx_splash)
			end
		end
		if player.solchar.istransformed
			if not (player.mo.eflags & MFE_UNDERWATER)
				player.mo.momz = $-(P_GetMobjGravity(player.mo)*7/12)
			end
			if not (player.marine.spin%3)
				local big = P_SpawnMobjFromMobj(player.mo, -FixedMul(64*FRACUNIT, cos(player.drawangle)), -FixedMul(64*FRACUNIT, sin(player.drawangle)), player.height/2, MT_MARINE_SUPER)
				big.extravalue1 = player.drawangle
				big.target = player.mo
				big.color = player.mo.color
				big.colorized = true
				S_StartSound(player.mo, sfx_bubbl1 + ((player.marine.spin/3)%5))
			end
		end
	else
		player.thrustfactor = 5
	end
	if (player.mo.state == S_MARINE_FLIP or player.mo.state == S_MARINE_FLIP2
	or (player.actionstate == 1 and not (player.actiontime % 5)))
		if player.actionstate != 1
			player.drawangle = R_PointToAngle2(0,0,player.mo.momx,player.mo.momy)
		end
		if player.mo.tics & 1
			local ghost = P_SpawnGhostMobj(player.mo)
			if not player.solchar.istransformed
				ghost.color = SKINCOLOR_AQUA
			end
			ghost.colorized = true
		else
			local ns = 6*player.mo.scale
			for i = 0, 15
				local fa = i*(ANGLE_180/8)
				local mobj = P_SpawnMobjFromMobj(player.mo,0,0,player.height/2,MT_THOK)
				if mobj and mobj.valid
					mobj.sprite = SPR_BUBL
					mobj.frame = $ + B
					mobj.fuse = TICRATE/2
					mobj.tics = mobj.fuse
					if player.solchar.istransformed
						mobj.frame = $|FF_FULLBRIGHT
						mobj.color = player.mo.color
					else
						mobj.color = SKINCOLOR_AQUA
					end
					mobj.colorized = true
					mobj.momz = FixedMul(sin(fa),ns)
					P_InstaThrust(mobj, player.drawangle+ANGLE_90,
					FixedMul(cos(fa),ns))
					P_Thrust(mobj, player.drawangle, ns)
					mobj.rollangle = fa
				end
			end
		end
	end
	if (player.marine.fly) and (player.pflags & PF_JUMPDOWN) and (player.pflags & PF_NOJUMPDAMAGE) and (P_MobjFlip(player.mo)*player.mo.momz < 0)
		if (player.panim != PA_ABILITY)
			if (player.marine.fly > TICRATE*3/4)
				player.mo.state = S_PLAY_FLY
				player.marine.cool = TICRATE/3
			else
				player.mo.state = S_MARINE_FLUTTER
				player.marine.cool = TICRATE/2
			end
			player.panim = PA_ABILITY
			player.pflags = $|P_GetJumpFlags(player)
		end
		if (player.marine.fly > TICRATE*3/2)
			player.mo.momz = 0
		else
			local gravy = P_GetMobjGravity(player.mo)
			player.mo.momz = $-(gravy*3/4)
			if (P_MobjFlip(player.mo)*player.mo.momz < FixedMul(8<<FRACBITS, gravy))
				player.mo.momz = FixedMul(8<<FRACBITS, gravy)
			end
			if not (player.marine.fly > TICRATE*3/4) and (player.mo.state != S_MARINE_FLUTTER)
				player.mo.state = S_MARINE_FLUTTER
				player.panim = PA_ABILITY
			end
		end
		if not player.solchar.istransformed
			player.marine.fly = $-(TICRATE/35)
		end
	end
	if not (player.pflags & PF_SPINNING) and ((player.marine.roll) or ((twodlevel or (player.realmo.flags2 & MF2_TWOD)) and P_IsObjectOnGround(player.mo) and (player.cmd.forwardmove < -20) and (player.speed > FixedMul(5<<FRACBITS, player.mo.scale))))
		player.pflags = $|PF_SPINNING|PF_SPINDOWN
		player.mo.state = S_PLAY_ROLL
		if not (player.marine.roll)
			S_StartSound(player.mo, sfx_spin)
		end
	end
	if P_PlayerInPain(player) or player.powers[pw_carry] or (player.exiting) or (player.gotflag) or (player.gotcrystal) or (player.playerstate != PST_LIVE)
		marineset(player)
		player.marine.run = 0
		return
	end
	if (player.mo.eflags & MFE_JUSTHITFLOOR)
		if (player.marine.soar)
			player.marine.run = TICRATE*2
		end
		marineset(player)
	end
	if (player.panim == PA_ABILITY)
		if not (leveltime%8)
			S_StartSound(player.mo, sfx_fmarin)
		end
		if not (player.marine.cool)
			if not (player.pflags & PF_JUMPDOWN) or ((not player.marine.fly) and not (player.marine.soar))
				player.mo.state = S_PLAY_FALL
			end
		else
			player.marine.cool = $-(TICRATE/35)
		end
	end
	if (player.marine.run >= TICRATE*2)
		player.marine.run = TICRATE*2
		marineghost(player)
	end
	if (FixedHypot(player.rmomx, player.rmomy) > getrushcharpspeed(player))
	and (P_IsObjectOnGround(player.mo) or player.pflags & PF_JUMPED) and not ((player.panim == PA_ABILITY) and (player.marine.fly)) and not player.glidetime
		player.marine.run = $+(TICRATE/35)
		if (player.solchar) and (player.solchar.istransformed)
			player.marine.run = $+(TICRATE/35)
		end
		if not (leveltime%2) and (FixedHypot(player.rmomx, player.rmomy) > FixedMul(52<<FRACBITS, player.mo.scale))
			player.marine.run = $+(TICRATE/35)
		end
	elseif (player.marine.run) and P_IsObjectOnGround(player.mo)
		player.marine.run = max(0, $-(TICRATE/12))
	end
	if (player.marine.soar)
		marineghost(player)
		player.marine.soar = player.solchar.istransformed and $ or $-(TICRATE/35)
		if (player.pflags & PF_JUMPDOWN) and (player.pflags & PF_NOJUMPDAMAGE)
			if (P_MobjFlip(player.mo)*player.mo.momz < FixedMul(8<<FRACBITS, player.mo.scale))
				P_SetObjectMomZ(player.mo, 8<<FRACBITS, false)
			end
			if (player.panim != PA_ABILITY)
				player.mo.state = S_PLAY_FLY
				player.pflags = $|P_GetJumpFlags(player)
				player.marine.cool = TICRATE/3
				player.panim = PA_ABILITY
			end
			if (player.marine.soar == TICRATE/35)
				player.marine.fly = (player.glidetime) and TICRATE*2 or TICRATE*3
				player.marine.used = true
			end
		end
	end
end)

//specials

addHook("AbilitySpecial", function(player)
	if not (player.marine) or (player.pflags & PF_SPINNING) or (player.gotflag) or (player.gotcrystal)
		return nil
	end
	//bubble flip
	if (player.mo.state == S_MARINE_SWIPE) and not (player.pflags & PF_THOKKED)
		player.mo.momx = $*4/5
		player.mo.momy = $*4/5
		
		player.pflags = $ & ~PF_JUMPED
		P_DoJump(player, false)
		player.pflags = $|PF_THOKKED & ~PF_NOJUMPDAMAGE
		player.mo.state = S_MARINE_FLIP

		S_StartSound(player.mo, sfx_jmarin)
		if player.pflags & PF_ANALOGMODE and player.pflags & PF_DIRECTIONCHAR
		and not player.cmd.forwardmove and not player.cmd.sidemove
			player.drawangle = player.marine.oldangle
		else
			player.drawangle = player.mo.angle
		end
		
		if FixedHypot(player.rmomx, player.rmomy) > 40*player.mo.scale
			P_Thrust(player.mo, player.drawangle, 16*player.mo.scale)
		else
			P_Thrust(player.mo, player.drawangle, 20*player.mo.scale)
		end
		player.weapondelay = 0
		player.marine.cool = 0
	//float
	elseif not (player.marine.cool) and (player.mo.state != S_MARINE_SWIPE)
		if (P_MobjFlip(player.mo)*player.mo.momz < 0)
			player.mo.momz = 0
		end
		if not (player.marine.soar)
			if (player.marine.run >= TICRATE*2)
				player.marine.run = 0
				player.marine.soar = TICRATE*3
				player.marine.used = false
				player.glidetime = 1
			elseif not (player.marine.used)
				player.marine.fly = (player.glidetime) and TICRATE*2 or TICRATE*3
				player.marine.used = true
			elseif not (player.solchar.istransformed)
				player.marine.fly = max(0, $-(TICRATE*3/4))
			end
		end
		if (player.marine.fly > TICRATE*3/4) or (player.marine.soar)
			player.mo.state = S_PLAY_FLY
			player.marine.cool = TICRATE/3
		else
			player.mo.state = S_MARINE_FLUTTER
			player.marine.cool = TICRATE/2
		end
		player.panim = PA_ABILITY
		player.pflags = $|PF_THOKKED|PF_NOJUMPDAMAGE|PF_CANCARRY & ~PF_SPINNING
		S_StartSound(player.mo, sfx_tmarin)
		if (player.revi)
			player.revi.spring = 2
		end
	end
	return true
end)

addHook("JumpSpecial", function(player)
	if not (player.marine)
		return nil
	end
	if (P_IsObjectOnGround(player.mo) or (player.mo.eflags & MFE_JUSTHITFLOOR)) and not (player.pflags & PF_JUMPDOWN)
		marineset(player)
	end	
end)

addHook("SpinSpecial", function(player)
	if not (player.marine)
		return nil
	end
	if not P_PlayerInPain(player) and not (player.pflags & PF_SPINDOWN) and not (player.weapondelay)
	and not (player.pflags & PF_SLIDING) and not (player.pflags & PF_SPINNING) and not (player.exiting)
	and (P_IsObjectOnGround(player.mo) or player.pflags & PF_NOJUMPDAMAGE)
	and not player.powers[pw_carry] and (player.mo.state != S_MARINE_JOJOKE)
		if not P_IsObjectOnGround(player.mo)
			//hi
		elseif (player.solchar.istransformed) or (player.mo.eflags & MFE_UNDERWATER)
			P_SetObjectMomZ(player.mo, 2*FRACUNIT, true)
		else
			P_SetObjectMomZ(player.mo, 5*FRACUNIT, true)
		end
		player.mo.state = S_MARINE_SWIPE
		S_StartSound(player.mo, sfx_tmarin)
		player.pflags = $|PF_JUMPED|PF_JUMPSTASIS & ~PF_NOJUMPDAMAGE
		player.panim = PA_DASH
		player.marine.angle = player.drawangle
		player.marine.oldangle = player.mo.angle
		player.marine.spin = 0
		if (player.marine.fly) and not (player.solchar.istransformed)
			player.marine.fly = max(0, $-(TICRATE/2))
		end
		if (player.revi)
			player.revi.spring = 2
		end
		if STR_HEAVY then
			player.powers[pw_strong] = STR_ANIM|STR_HEAVY|STR_SPRING|STR_SPIKE
		end
		local hitbox = P_SpawnMobjFromMobj(player.mo, 0, 0, 0, MT_MARINE_HIT)
		hitbox.target = player.mo
		player.weapondelay = TICRATE*5/8
	end
	return true
end)

addHook("PlayerHeight", function(player)
	if not (player.marine)
		return nil
	end
	if (player.solchar.istransformed and ((player.panim == PA_RUN) or (player.panim == PA_DASH))) or (player.mo.state == S_MARINE_SWIPE)
		return P_GetPlayerSpinHeight(player)
	end
	if (player.mo.state == S_PLAY_WAIT)
		if redrover(player.mo) and (redrover(player.mo).flags & ML_NOCLIMB) and not ((player.mo.eflags & MFE_TOUCHWATER) and not (player.mo.eflags & MFE_UNDERWATER))
			player.pflags = $|PF_STARTDASH
		end
		return P_GetPlayerSpinHeight(player)
	end
end)

addHook("PlayerCanEnterSpinGaps", function(player)
	if (player.marine)
		return true
	end
end)

//hitbox

addHook("MobjThinker", function(hit)
	if (hit.target) and hit.target.player and (hit.target.state == S_MARINE_SWIPE)
		A_CapeChase(hit, (-16*FRACUNIT)+0, 0)
		hit.target.player.pflags = $|PF_SPINDOWN
	else
		pcall(P_RemoveMobj, hit)
	end
end, MT_MARINE_HIT)

addHook("MobjCollide", function(marine, inflictor)
	if inflictor.valid and marine.threshold < 20
	and ((inflictor.flags & MF_MISSILE) or (inflictor.type == MT_UNIBALL)) and (inflictor.type != MT_LASER)
	and (marine.target) and not (inflictor.target and (inflictor.target == marine.target))
	and not (inflictor.z > marine.z+marine.height) and not (marine.z > inflictor.z+inflictor.height)
		marine.threshold = $ + 1
		inflictor.flags = $|MF_MISSILE
		inflictor.target = marine.target
		if (inflictor.fuse)
			inflictor.fuse = max(TICRATE, inflictor.fuse)
		end
		inflictor.color = marine.target.color
		inflictor.momz = -inflictor.momz
		inflictor.momy = -inflictor.momy
		inflictor.momx = -inflictor.momx
	end
	return false
end, MT_MARINE_HIT)

addHook("MobjMoveCollide", function(marine, spring)
	if (marine.target) and spring.valid
		if not (spring.z > marine.z+marine.height) and not (marine.z > spring.z+spring.height)
			if (spring.type == MT_SPIKE) or (spring.type == MT_WALLSPIKE)
				P_KillMobj(spring, marine, marine)
				return false
			end
			if (spring.flags & MF_SPRING) and not (marine.target.eflags & MFE_SPRUNG) and (spring.info.painchance != 3)
				marine.target.player.pflags = $ & ~PF_JUMPED & ~PF_THOKKED
				marine.target.state = S_PLAY_MELEE_LANDING
				marine.target.tics = 1
				P_DoSpring(spring, marine.target)
				if (spring.info.mass == 0)
					P_SetObjectMomZ(marine.target, -16*FRACUNIT, true)
					marine.target.player.marine.roll = true
				end
			end
			if (spring.flags & MF_SHOOTABLE) and not (spring.flags2 & MF2_FRET) and not (spring.player)
				if (spring.info.spawnhealth > 1) and (spring.flags & (MF_ENEMY|MF_BOSS)) 
					marine.target.momy = -$
					marine.target.momx = -$
				end
				P_DamageMobj(spring, marine, marine.target)
			end
		end
	end
	return false
end, MT_MARINE_HIT)

addHook("MobjLineCollide", function(marine, line)
	for _, sector in ipairs({line.frontsector, line.backsector})
		marinebust(marine, sector)
	end
end, MT_MARINE_HIT)

addHook("MobjMoveCollide", function(marine, spring)
	if (spring.flags & MF_SPRING) and (marine.state == S_MARINE_SWIPE) and (spring.info.painchance != 3)
		return false
	end
end, MT_PLAYER)

addHook("ShouldDamage", function(marine, inflictor)
	if (marine.state == S_MARINE_SWIPE) and (inflictor) and (inflictor.flags & (MF_MISSILE|MF_ENEMY|MF_BOSS))
		return false
	end
end, MT_PLAYER)

//misc

addHook("MobjRemoved", function(sol)
	CBW_Battle.Item.BubbleBurst(sol)
end, MT_MARINE_PUNCH)

addHook("MobjThinker", function(bubble)
	if bubble.valid and bubble.target
		if (bubble.target.state != S_MARINE_SWIPE)
			if (rusheffects.value) and P_TryMove(bubble, bubble.x, bubble.y, bubble.z)
				local float = P_SpawnMobjFromMobj(bubble, 0, 0, 0, MT_MARINE_FLOAT)
				float.target = bubble.target
				float.color = bubble.color
				float.colorized = true
				float.fuse = TICRATE*4
			end
			pcall(P_RemoveMobj, bubble)
			return
		end
		bubble.extravalue1 = $+ANG1
		local bdistance = 64*bubble.target.scale
		local bspeed = FixedAngle(bubble.extravalue1*24)
		P_TeleportMove(bubble, bubble.target.x+FixedMul(bdistance, cos(bspeed)), bubble.target.y+FixedMul(bdistance, sin(bspeed)), bubble.target.z+bubble.target.height/3) 
	end
end, MT_MARINE_SUPER)

addHook("MobjMoveCollide", bubbler, MT_MARINE_SUPER)
addHook("MobjMoveCollide", bubbler, MT_MARINE_FLOAT)

//bot

addHook("BotRespawn", function(blazmo, marimo)
	if (gamestate != GS_LEVEL) return end //thanks srb2
	local player2 = marimo.player
	if (marimo.skin != "marine") or not (player2.marine)
		return nil
	end
	local player1 = blazmo.player
	if (player2.bot == BOT_2PAI) and P_IsObjectOnGround(marimo)
		player2.charability = CA_FLY
		player2.jumpfactor = skins[player2.skin].jumpfactor*5/3
	else
		player2.charability = CA_NONE
		player2.jumpfactor = skins[player2.skin].jumpfactor
		if (player2.bot == BOT_2PAI)
			player2.pflags = $|PF_SPINDOWN
		end
	end
	if (player1.powers[pw_carry] == CR_PLAYER) and (blazmo.tracer == marimo)
		if not (player2.marine.help)
			player2.marine.soar = TICRATE*2
			player2.glidetime = 1
			player2.marine.help = true
		end
		player2.cmd.buttons = $|BT_JUMP
	elseif (player2.marine.help)
		player2.marine.soar = 0
	end
end)
